Repository navigation
Show the Policy spellings that type-check - #334
Merged
Merged
Conversation
The package ships py.typed, and every Policy configuration example in
the guide needed a # type: ignore[arg-type]. Five spellings failed, not
the two first noticed: script_orders={}, segment_scripts=(),
nickname_delimiters={...}, maiden_delimiters={...} and the
dict(DEFAULT_SCRIPT_ORDERS) | {...} override. A bare SET literal for a
delimiter field is the one most likely to bite, being the spelling
anyone reaches for first.
The cause is not sloppiness and is not fixed here: a dataclass field
annotation types both the __init__ parameter and the attribute, and
Policy normalizes in __post_init__, so those two types genuinely
differ. The annotation stays the storage type, because reading the
attribute is the commoner operation and a union there would cascade to
every reader -- the same reasoning that already kept
empty_attribute_default narrow.
What changes is which spelling the docs teach. Each one has a
type-clean equivalent that means exactly the same thing, verified
under mypy. Union spellings are untouched: frozenset | set is already
frozenset, so DEFAULT_NICKNAME_DELIMITERS | {...} was always clean.
The script_orders override becomes (*DEFAULT_SCRIPT_ORDERS, (Script.HAN,
GIVEN_FIRST)), which depends on a later entry replacing an earlier one.
That was emergent from _validated_script_orders building a dict before
sorting rather than a stated contract, so prose telling readers to
append needed it pinned: a test now asserts the override, the survival
of the other entries, one entry per script, and equality with the
mapping spelling it replaces.
The note in customize.rst said the opposite of what is true now; it
explains the storage-vs-constructor split instead.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #334 +/- ##
=======================================
Coverage 98.48% 98.48%
=======================================
Files 41 41
Lines 2832 2832
=======================================
Hits 2789 2789
Misses 43 43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
sauliusbc
pushed a commit
to SavantEnvs/python-nameparser
that referenced
this pull request
Aug 22, 2026
Four sites, two kinds of drift. derek73#334 moved the guide to the Policy spellings that type-check, but only the hand-written .rst. Four _policy.py docstrings teach the old ones and autodoc renders them into modules.html, so the API reference and the guide disagreed on the same page. Worse, derek73#337's new runtime warning hands the user Policy(segment_scripts=()) -- the library telling you, in a package that ships py.typed, to write an arg-type error. A test now pins the offered spelling; the warning tests matched on 'ja_segmenter' and never checked the actionable half of the message. Two stage headers understated their inputs. _extract declared only the two delimiter policy fields while reading three Lexicon suffix fields through _suffix_shaped, which is not a detail: that is the mechanism letting a clause's content overrule the delimiter, and derek73#335 would extend it. _post_rules omitted Policy.middle_as_family. Checked mechanically rather than by eye: every stage's declared Reads against the policy and lexicon attributes it actually touches. _script_segment, _tokenize, _assign, _group and _classify were already accurate. _vocab has no Reads line by design, being a helper whose predicates take vocabulary explicitly. Its 'no state' claim still holds.
derek73
added a commit
that referenced
this pull request
Oct 27, 2026
…NTS.md files (#909) * Move the ambiguity-emitter rules from AGENTS.md to nameparser/_pipeline/AGENTS.md The Ambiguities bullet had grown to 1,488 words in the root file, which every session loads. It is guidance for work inside the pipeline stages, so it moves, unchanged in substance and split under subheadings, into a nested AGENTS.md that loads when a session reads a stage. Root keeps the principle and pointers to it and to mechanisms.md's contract entry; the two test comments quoting "Pin the decision, not the vocabulary" now name the new file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cut AGENTS.md's design-philosophy paragraph to its doctrine and an index of the script rules The paragraph had reached 1,458 words, most of it the glued-honorific peel's comma reach (#312, #319, #613) restated from code. Root keeps the positional, never-detect-language doctrine, the script-itself exception and its three-way test, and lists each behavior it licenses with the rule that states it. The facts no rule or decisions entry held move to decisions/W2 as a dated entry, every parse re-run today: the reach and second-condition examples, where the credential lands by spelling, why the run predicate is not the token predicate pluralized, what lenient_comma_suffixes=False does, the four drifts that made the peel ask _comma.decide, and that neither policy opt-out reaches the peel. The #760 entry, which deferred two of those facts to this paragraph, now says where they went. The opt-out example is spelled segment_scripts=frozenset(), the denylist having refused the () spelling (#334). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Move the spelling sweep from AGENTS.md to a nameparser/AGENTS.md The spelling sweep (1,326 words) is needed by any change to a word class, vocabulary-only changes included, so it moves to a nested AGENTS.md at nameparser/ rather than under _pipeline/: reading a vocab TOML loads it too. The text moves unchanged in substance, split under a subheading per class (numerals, credentials and titles, separators, full stops inside a word, capitals, adjacent pairs). Root keeps the lead and a pointer; the Workflow paragraph, which says where a missed shape is recorded, and the two cases.py notes that cite the sweep now name the new file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix what the design-docs review found in the relocation - Restore #337's `Policy(segment_scripts=())` in the validation bullet: the frozenset() replacement meant for the new opt-out example also hit this history line, which records () as the spelling that shipped wrong. - Retarget the stale pointers: decisions/C14 (honorific-peel paragraph), _pieces.py and cases.py:2859 (the spelling sweep's #603 example and separator spacing). - Root index: restore the katakana reason (its script settles nothing, not "foreign"), cite W1's interpunct Except for "never segments", name decisions/T3 as the nakaguro's history, keep 高橋+一郎. - decisions/W2: the #760 entry's last pointer, and the facts the cut left only in usage.rst or nowhere (#319's failure mechanism, the pre-#319 readings, why 博士/殿/君 are spaced-only), re-run 2026-10-27. - nameparser/AGENTS.md: give the moved "That holds" sentence an antecedent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cite #909 in decisions/W2's 2026-10-27 entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Record in AGENTS.md that a whole-file replace rewrites history lines (#909) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
nameparser ships
py.typed, and everyPolicyconfiguration example in the docs needed a# type: ignore[arg-type]. This changes the docs to teach the spellings that check clean.Five spellings failed, not the two originally noticed:
Policy(script_orders={})dict→tuplePolicy(segment_scripts=())tuple→frozensetPolicy(nickname_delimiters={("[", "]")})set→frozensetPolicy(maiden_delimiters={("(", ")")})set→frozensetPolicy(script_orders=dict(DEFAULT_SCRIPT_ORDERS) | {...})dict→tupleThe bare set literal for a delimiter field is the one most likely to bite in practice — it is the spelling anyone reaches for first, and it appeared in
concepts.rstas the introductory example.What is not changed, and why
The annotations. A dataclass field annotation types both the
__init__parameter and the attribute, andPolicynormalizes in__post_init__(its validators takeobject), so those two types genuinely differ. Python cannot express that with one annotation.The annotation stays the storage type, because reading the attribute is the commoner operation and a union there would cascade to every reader — the same reasoning that already kept
empty_attribute_defaultnarrow. This is a real expressiveness gap, not sloppiness, and it applies to every normalizing field.Union spellings were already fine and are untouched:
frozenset | setisfrozenset, soDEFAULT_NICKNAME_DELIMITERS | {("{", "}")}always checked clean.The one spelling that needed a contract first
The
script_ordersoverride becomes:which depends on a later entry replacing an earlier one. That behavior was emergent —
_validated_script_ordersbuilds adictbefore sorting, and the comment there justifies it for hashability, not for override semantics. Prose telling readers to append is prose that depends on it, sotests/v2/test_policy.pynow pins the override, the survival of the other entries, one-entry-per-script, and equality with the mapping spelling it replaces.Verification
mypyclean.uv run sphinx-build -b doctest docs— exit 0.uv run pytest -q— 3060 passed (was 3059; one new test).uv run mypy,uv run ruff check— clean.Note for review
The
.. note::incustomize.rstpreviously told readers the readable spellings need a# type: ignore. It now explains the storage-vs-constructor split instead, and says the wider spellings still parse identically for anyone not running a type checker.I did not add a release-log entry: no behavior changed. Arguably the newly-contracted last-wins rule is a promise the library now makes and could warrant a line — happy to add one if you read it that way.
🤖 Generated with Claude Code